/**
Event Manager
=============

:class:`liwe.event_manager` handles the module / class events like standard DOM event model.

To use this, you have to provide the object to link :class:`liwe.event_manager` to and a list of valid events to be handled.

	.. code-block:: javascript
		
		var my_obj = new MyObject ();

		liwe.event_manager ( my_obj, [ 'click', 'over', 'out' ] )


The object where :class:`event_manager` is attached to, will have the following methods:

	- :meth:`liwe.event_manager.valid_events()` - To define the events the object will react to.
	- :meth:`liwe.event_manager.add_event()` - To add dynamically a new event.
	- :meth:`liwe.event_manager.addEventListener()` - To attach a new event listener.
	- :meth:`liwe.event_manager.removeEventListener()` - To remove a previously attached event listener.
	- :meth:`liwe.event_manager.dispatchEvent()` - To dispatch an event.

**/
liwe.event_manager = function ( obj_ref, valid_events, debug )
{
	var self = this;

	this._obj_ref = obj_ref;
	this._events = {};
	this._sys_events = {};
	this._sys_before_events = {};
	this._events_list = [];

	this.debug = !! debug;

	this._obj_ref.addEventListener = function ( type, cback, _system, _before ) { return self.addEventListener ( type, cback, _system, _before ); };
	this._obj_ref.removeEventListener = function ( type, cback ) { return self.removeEventListener ( type, cback ); };
	this._obj_ref.dispatchEvent = function ( type, source_event, params ) { return self.dispatchEvent ( type, source_event, params ); };
	this._obj_ref.valid_events = function ( events_list ) { return self.valid_events ( events_list ); };

	if ( valid_events ) this.valid_events ( valid_events );
};

liwe.event_manager.prototype.valid_events = function ( events_list )
{
	this._events_list = events_list;
	this.clear ( true );
};

liwe.event_manager.prototype.add_event = function ( ev )
{
	this._events_list.push ( ev );
	this._events [ ev ] = [];
	this._sys_events [ ev ] = [];
	this._sys_before_events [ ev ] = [];
};

liwe.event_manager.prototype.clear = function ( clear_system )
{
	var el = this._events_list;
	var t, l = el.length;

	this._events = {};

	if ( clear_system ) 
	{
		this._sys_events = {};
		this._sys_before_events = {};
	}

	for ( t = 0; t < l; t ++ ) 
	{
		this._events [ el [ t ] ] = [];
		if ( clear_system ) 
		{
			this._sys_events [ el [ t ] ] = [];
			this._sys_before_events [ el [ t ] ] = [];
		}
	}
};

liwe.event_manager.prototype.addEventListener = function ( type, cback, _system, _before )
{
	if ( ! this._check_event_name ( type ) ) return false;

	if ( ! _system )
		this._events [ type ].push ( cback );
	else 
	{
		if ( _before )
			this._sys_before_events [ type ].push ( cback );
		else
			this._sys_events [ type ].push ( cback );
	}

	return true;
};

liwe.event_manager.prototype.removeEventListener = function ( type, cback )
{
	if ( ! this._check_event_name ( type ) ) return false;

	var t, l, evt = this._events [ type ];
	var b = [];

	l = evt.length;

	for ( t = 0; t < l; t++ )
	{
		if ( evt [ t ] == cback )
		{
			b = b.concat ( evt.slice ( 0, t ), evt.slice ( t + 1 ) )
			this._events [ type ] = b;
			return true;
		}
	}

	return false;
};

liwe.event_manager.prototype.dispatchEvent = function ( type, source_event, params )
{
	if ( ! this._check_event_name ( type ) ) return false;

	var res, self = this;

	function _dispatch ( evt )
	{
		var my_evt, res, t;

		for ( t = evt.length - 1; t >= 0; t-- )
		{
			my_evt = new liwe.event_manager.event ( self._obj_ref, type, source_event, params );
			res = evt [ t ].call ( self._obj_ref, my_evt );
			if ( my_evt._stopped ) res = false;
			if ( res === false ) return false;
		}

		return res;
	};

	res = _dispatch ( this._sys_before_events [ type ] );
	if ( res === false ) return false;

	res = _dispatch ( this._events [ type ] );
	if ( res === false ) return false;

	res = _dispatch ( this._sys_events [ type ] );
	
	return res;
};

liwe.event_manager.prototype._check_event_name = function ( type )
{
	if ( type in this._events ) return true;

	if ( this.debug ) console.error ( "EventManager: wrong event type: %s on %s", type, this._obj_ref );

	return false;
};

liwe.event_manager.event = function ( target, event_name, orig_event, params )
{
	this.target = target;
	this.name   = event_name;
	this.params = params;
	this.source = orig_event;

	this._stopped = false;
};

liwe.event_manager.event.prototype.stopPropagation = function ()
{
	this._stopped = true;
};
